/*
 ============================================================================
 Name		: stopwatchContainer.cpp
 Author	  : Huang.haiyang
 
     This is free software; you can redistribute it and/or
     modify it under the terms of the GNU Lesser General Public
	 License as published by the Free Software Foundation; either
     version 2 of the License, or (at your option) any later version.
 
     This software is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     Lesser General Public License for more details.
     
 Description : Container control implementation
 ============================================================================
 */

// INCLUDE FILES
#include "..\stopwatchView.h"
#include "resource\ImageCache.h"
#include "utils\ImageUtils.h"
#include "view\ButtonView.h"
#include "service\TimerService.h"

#include <eikenv.h>
#include <eikappui.h>

// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------
// CstopwatchContainer::ConstructL(const TRect& aRect)
// EPOC two phased constructor
// ---------------------------------------------------------
//
void CStopwatchView::ConstructL(const TRect& aRect)
	{
	CreateWindowL();

	SetRect(aRect);
	
	iTimerService = new (ELeave) service::TimerService(this);
	
	TRect barArea = GetButtonArea();
	TSize size;
	TRect target;
	
	view::CButtonView* leftBtn = new (ELeave) view::CButtonView();
	leftBtn->SetOnClickListener(this);
	leftBtn->SetMapKey(EKeyDevice0);
	leftBtn->SetImageSrc(_L("res\\reset.png"));
	size = leftBtn->Measure(barArea.Size());
	target.SetRect(barArea.iTl.iX, barArea.iBr.iY - size.iHeight,
			barArea.iTl.iX + size.iWidth, barArea.iBr.iY);
	size = barArea.Size() - size;
	leftBtn->ConstructL(target, this);
	iControls.AppendL(leftBtn);
	
	//centeral start/next button
	view::CButtonView* midBtn = new (ELeave) view::CButtonView();
	midBtn->SetOnClickListener(this);
	midBtn->SetMapKey(EKeyDevice3);
	midBtn->SetImageSrc(_L("res\\start.png"));
	size = midBtn->Measure(size);
	target.SetRect(TPoint(barArea.iTl.iX + (barArea.Width() - size.iWidth)/2,
			barArea.iBr.iY - size.iHeight),
			size);
	size = barArea.Size() - size;
	midBtn->ConstructL(target, this);
	iControls.AppendL(midBtn);
		
	view::CButtonView* rightBtn = new (ELeave) view::CButtonView();
	rightBtn->SetOnClickListener(this);
	rightBtn->SetMapKey(EKeyDevice1);
	rightBtn->SetImageSrc(_L("res\\stop.png"));
	size = rightBtn->Measure(size);
	target.SetRect(barArea.iBr.iX - size.iWidth, barArea.iBr.iY - size.iHeight,
				barArea.iBr.iX, barArea.iBr.iY);
	rightBtn->ConstructL(target, this);
	iControls.AppendL(rightBtn);
			
	ActivateL();
	}

// Destructor
CStopwatchView::~CStopwatchView()
	{
	iRecords.Reset();
	delete iTimerService;
	iControls.ResetAndDestroy();
	}

void CStopwatchView::OnClick(CCoeControl* aView)
	{
	if (aView == iControls[2])
		{
		OnStop();
		//CEikonEnv::Static()->EikAppUi()->HandleCommandL(EEikCmdExit);
		}
	else if (aView == iControls[1])
		{
		if (iTimerService->Runing())
			{			
			OnNext();
			}
		else
			{
			OnStart();
			}
		}
	else if (aView == iControls[0])
		{
		OnReset();
		}
	}

void CStopwatchView::OnReset()
	{
	iTimerService->Reset();		
	iSpanInMills = 0;
	iRecords.Reset();
	view::CButtonView* midBtn = (view::CButtonView*)iControls[1];
	midBtn->SetImageSrc(_L("res\\start.png"));
	DrawNow();
	}

void CStopwatchView::OnStart()
	{
	view::CButtonView* midBtn = (view::CButtonView*)iControls[1];
	midBtn->SetImageSrc(_L("res\\next.png"));
	iTimerService->Start();
	}

void CStopwatchView::OnStop()
	{
	iTimerService->Stop();
	view::CButtonView* midBtn = (view::CButtonView*)iControls[1];
	midBtn->SetImageSrc(_L("res\\start.png"));
	iScrollRecordBottom = iRecords.Count();
	DrawNow();
	}

void CStopwatchView::OnNext()
	{
	TUint32 stamp = iTimerService->Next();
	iRecords.Append(stamp);
	}
	
TKeyResponse CStopwatchView::OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType)
	{
	for (TInt i = 0; i < iControls.Count(); i++)
		{
		if (EKeyWasConsumed == iControls[i]->OfferKeyEventL(aKeyEvent, aType))
			{
			return EKeyWasConsumed;
			}
		}
	
	if (aType == EEventKey && !iTimerService->Runing())
		{
		if (aKeyEvent.iCode == EKeyUpArrow)
			{
			Scroll(-1);
			return EKeyWasConsumed;
			}
		else if (aKeyEvent.iCode == EKeyDownArrow)
			{
			Scroll(1);
			return EKeyWasConsumed;
			}
		}		
	
	return CCoeControl::OfferKeyEventL(aKeyEvent, aType);
	}

// ---------------------------------------------------------
// CstopwatchContainer::SizeChanged()
// Called by framework when the view size is changed
// ---------------------------------------------------------
//
void CStopwatchView::SizeChanged()
	{
	}

// ---------------------------------------------------------
// CstopwatchContainer::CountComponentControls() const
// ---------------------------------------------------------
//
TInt CStopwatchView::CountComponentControls() const
	{
	return iControls.Count();
	}

// ---------------------------------------------------------
// CstopwatchContainer::ComponentControl(TInt aIndex) const
// ---------------------------------------------------------
//
CCoeControl* CStopwatchView::ComponentControl(TInt aIndex) const
	{
	return iControls[aIndex];
	}

// ---------------------------------------------------------
// CstopwatchContainer::Draw(const TRect& aRect) const
// ---------------------------------------------------------
//
void CStopwatchView::Draw(const TRect& aRect) const
	{
	CWindowGc& gc = SystemGc();
	DrawBackground(gc, aRect);
	DrawRecords(gc, GetRecrodArea());
	DrawTimer(gc, GetTimerArea());
	}

// ---------------------------------------------------------
// CstopwatchContainer::HandleControlEventL(
//	 CCoeControl* aControl,TCoeEvent aEventType)
// ---------------------------------------------------------
//
void CStopwatchView::HandleControlEventL(CCoeControl* /*aControl*/,
		TCoeEvent /*aEventType*/)
	{
	
	}

void CStopwatchView::OnTick(TUint32 aMills)
	{
	iSpanInMills = aMills;
	DrawNow();
	}

void CStopwatchView::DrawBackground(CWindowGc& aGc, const TRect& aInvalid) const
	{
	aGc.SetBrushColor(TRgb(0x658365));
	aGc.SetBrushStyle(CWindowGc::ESolidBrush);
	aGc.SetPenStyle(CWindowGc::ENullPen);
	
	aGc.DrawRect(Rect());
	}

void CStopwatchView::DrawTimer(CWindowGc& aGc, const TRect& aRect) const
	{
	TRect rc;	
	TUint32 min, sec, quarter;
	
	MillsToTime(iSpanInMills, min, sec, quarter);
	
	TUint8 digitMinTen = min / 10;
	TUint8 digitMin = min - digitMinTen * 10;
	
	TUint8 digitSecTen = sec / 10;
	TUint8 digitSec = sec - digitSecTen * 10;
	
	TUint8 digitQuarterTen = quarter / 10;
	TUint8 digitQuarter = quarter - digitQuarterTen * 10;
	
	rc = aRect;
	
	//draw minutes
	DrawDigit(aGc, digitMinTen, rc);
	DrawDigit(aGc, digitMin, rc);
	DrawUpperCommas(aGc, rc);
	DrawDigit(aGc, digitSecTen, rc);
	DrawDigit(aGc, digitSec, rc);
	DrawUpperCommas(aGc, rc);
	DrawUpperCommas(aGc, rc);
	rc.iTl.iX -= 10;
	DrawSmallDigit(aGc, digitQuarterTen, rc);
	DrawSmallDigit(aGc, digitQuarter, rc);
	}

void CStopwatchView::DrawDigit(CWindowGc& aGc, TUint8 aDigit, TRect& aRect) const
	{
	data::CPNG* image;
	TSize imgSize;
	TInt digitWidth;
	TRect imageSrc;
	image = resource::CImageCache::getInstance()->GetImage(_L("res\\big_digits.png"));
	if (!image || !image->iRawFore)
		{
		return;
		}
	imgSize = image->iRawFore->SizeInPixels();
	digitWidth = imgSize.iWidth / 10;
	
	imageSrc.SetRect(TPoint(digitWidth * aDigit, 0), TSize(digitWidth, imgSize.iHeight));
	
	aGc.BitBltMasked(TPoint(aRect.iTl.iX, aRect.iBr.iY - imgSize.iHeight),
			image->iRawFore, imageSrc, image->iRawMask, ETrue);
	
	aRect.iTl.iX += digitWidth;
	}

void CStopwatchView::DrawSmallDigit(CWindowGc& aGc, TUint8 aDigit, TRect& aRect) const
	{
	data::CPNG* image;
	TSize imgSize;
	TInt digitWidth;
	TRect imageSrc;
	image = resource::CImageCache::getInstance()->GetImage(_L("res\\small_digits.png"));
	if (!image || !image->iRawFore)
		{
		return;
		}
	imgSize = image->iRawFore->SizeInPixels();
	digitWidth = imgSize.iWidth / 10;
	
	imageSrc.SetRect(TPoint(digitWidth * aDigit, 0), TSize(digitWidth, imgSize.iHeight));
	
	aGc.BitBltMasked(TPoint(aRect.iTl.iX, aRect.iBr.iY - imgSize.iHeight), 
			image->iRawFore, imageSrc, image->iRawMask, ETrue);
	
	aRect.iTl.iX += digitWidth;
	}

void CStopwatchView::DrawUpperCommas(CWindowGc& aGc, TRect& aRect) const
	{
	data::CPNG* image;
	TSize imgSize;
	image = resource::CImageCache::getInstance()->GetImage(_L("res\\upper_comas.png"));
	if (!image || !image->iRawFore)
		{
		return;
		}
	imgSize = image->iRawFore->SizeInPixels();
	
	aGc.BitBltMasked(TPoint(aRect.iTl.iX - imgSize.iWidth, aRect.iTl.iY),
			image->iRawFore, TRect(imgSize), image->iRawMask, ETrue);
	
	aRect.iTl.iX += imgSize.iWidth;
	}

void CStopwatchView::DrawRecords(CWindowGc& aGc, const TRect& aRect) const
	{
	data::CPNG* image;
	TSize imgSize;
	image = resource::CImageCache::getInstance()->GetImage(_L("res\\record_bg.png"));
	if (!image || !image->iRawFore)
		{
		return;
		}
	imgSize = image->iRawFore->SizeInPixels();
	
	aGc.BitBltMasked(TPoint(aRect.iTl.iX + (aRect.Width() - imgSize.iWidth)/2, 
			    aRect.iTl.iY),
				image->iRawFore, TRect(imgSize), image->iRawMask, ETrue);
	
	const CFont* font = CEikonEnv::Static()->NormalFont();
	aGc.SetBrushStyle(CWindowGc::ENullBrush);
	aGc.SetPenStyle(CWindowGc::ESolidPen);
	aGc.SetPenColor(TRgb(0x858d85));
	aGc.UseFont(font);
	
	TInt count = iRecords.Count();
	TInt totalHeight = count * font->HeightInPixels();
	if (totalHeight <= (aRect.Height() - 30))
		{
		TRect rRecord(aRect.iTl.iX, aRect.iTl.iY + 25,
					aRect.iBr.iX,
					aRect.iTl.iY + 25 + font->HeightInPixels());
		for (TInt i = 0; i < count; i++)
			{
			TUint32 min, sec, quarter;
			TBuf<50> buf;
			MillsToTime(iRecords[i], min, sec, quarter);
			buf.Format(_L("%02d    %02d'%02d\"%02d"), i+1, min, sec, quarter);
			aGc.DrawText(buf, rRecord, 
					(rRecord.Height() + font->AscentInPixels())/2,
					CWindowGc::ECenter, 0);
			rRecord.Move(0, font->HeightInPixels());
			}
		}
	else
		{
		TRect rRecord(aRect.iTl.iX, aRect.iBr.iY - 5 - font->HeightInPixels(),
					aRect.iBr.iX,
					aRect.iBr.iY - 5);
		if (!iTimerService->Runing())
			{
			count = iScrollRecordBottom;
			}
		
		for (TInt i = count - 1; i >= 0; i--)
			{
			TUint32 min, sec, quarter;
			TBuf<50> buf;
			MillsToTime(iRecords[i], min, sec, quarter);
			buf.Format(_L("%02d    %02d'%02d\"%02d"), i+1, min, sec, quarter);
			aGc.DrawText(buf, rRecord, 
					(rRecord.Height() + font->AscentInPixels())/2,
					CWindowGc::ECenter, 0);
			rRecord.Move(0, - font->HeightInPixels());
			if (rRecord.iTl.iY < (aRect.iTl.iY + 25))
				break;
			}
		}
	
	aGc.DiscardFont();
	}

void CStopwatchView::Scroll(TInt aStep)
	{
	TRect area = GetRecrodArea();
	TInt itemsCanDisplay = (area.Height() - 30) / CEikonEnv::Static()->NormalFont()->HeightInPixels();
	
	if (iRecords.Count() < itemsCanDisplay)
		{
		return;
		}
	
	if (aStep < 0 && iScrollRecordBottom - itemsCanDisplay <= 0)
		{
		return;
		}
	else if (aStep > 0 && iScrollRecordBottom >= iRecords.Count())
		{
		return;
		}
	
	iScrollRecordBottom += aStep;
	DrawNow();
	}

TRect CStopwatchView::GetTimerArea() const
	{
	TRect rTimer;
	rTimer.SetRect(TPoint(Rect().iTl.iX + (Rect().Width() - 164) / 2, 
			Rect().iTl.iY + (Rect().Height() - 30 - 112 - 67)/2), 
			TSize(164, 67));
	return rTimer;
	}

TRect CStopwatchView::GetRecrodArea() const
	{
	TRect rRecord;
	rRecord.SetRect(Rect().iTl.iX + 5, Rect().iBr.iY - 30 - 118,
			Rect().iBr.iX - 5, Rect().iBr.iY - 30);
	
	return rRecord;
	}

TRect CStopwatchView::GetButtonArea() const
	{
	TRect rButton;
	rButton.SetRect(Rect().iTl.iX, Rect().iBr.iY - 30,
			Rect().iBr.iX, Rect().iBr.iY);
	return rButton;
	}

void CStopwatchView::MillsToTime(TUint32 aMills, TUint32& aMin, 
			TUint32& aSec, TUint32& aQuarter)
	{
	aMin = aMills / (1000 * 60);
	aSec = aMills / 1000 - aMin * 60;
	aQuarter = (aMills - aMin * 1000 * 60 - aSec * 1000) / 10;
	}
